<!DOCTYPE html>
<html>
  <head>
    <meta content="text/html; charset=windows-1252" http-equiv="content-type">
    <title>Cofe3b</title>
  </head>
  <body>
    <h4><br>
    </h4>
    <h2>Introductions</h2>
    <p><br>
    </p>
    <h2>Index</h2>
    <p>This article is mainly written for describing the internal workings for
      Cofe3, for how to use Cofe3, please read Part I instead.</p>
    <h4>Part II</h4>
    <ul>
      <li>Features</li>
      <li>Cofe3.Core</li>
      <ul>
        <li>Property</li>
        <li>Entry </li>
        <li>Volume</li>
        <li>Events</li>
        <li>CofeServices</li>
      </ul>
      <li>Cofe3.Data</li>
      <ul>
        <li>IRepository</li>
        <li>DatabaseEntry</li>
        <li>DatabaseConfigurator</li>
      </ul>
      <li>Cofe3.Web</li>
      <ul>
        <li>FeedServices</li>
        <li>HttpMessageFormatterManager</li>
      </ul>
    </ul>
    <h2>Features</h2>
    Because I've been coding this for a while, I have no idea what others
    offers, this is a list of properties that I found interesting: <br>
    <h3>
      Not tied to file system</h3>
    After so much work to make the properties cachable in database,
    accessible using web services, why limit it to only file system?&nbsp; That
    was a question I asked when the previous version of Cofe is mostly
    completed.&nbsp; So version 2 was scapped and version 3 was rewritten.&nbsp;
    The
    result, Cofe.IO is separated implementation.&nbsp; Cofe3 can have multiple
    implementations at a time, all implementations can participate the path
    parsing, properties caching and web exposing features like Cofe.IO.<br>
    <h3>
      Multiple source of properties</h3>
    Just like FileSystemInfo in .net framework, Cofe.IO provide properties
    related to I/O, for examples, file size, stream etc.&nbsp; But thats not
    sufficient because this is a general purpose library.&nbsp; So, one can
    define secondary property provider to supply additional properties,
    like md5 of a file, image size etc.<br>
    <h3>
      Entry implements multiple interfaces</h3>
    One entry (ICofeSystemInfo) can support different interfaces, for
    examples, a zip archive is both IFileInfo and IDirectoryInfo, while a
    text file is just IFileInfo.&nbsp; This is definable in external projects,
    e.g. a jpg image is IFileInfo in Cofe.IO and IImageInfo in Cofe.Media.<br>
    Thanks to EFCodeFirst, properties in implementer of these interfaces
    can be written to database directly (e.g. dbo.FileInfo table), <br>
    <h3>
      Most properties are cachable and searchable</h3>
    The reason for caching entries in database, beside improving listing
    speed, it allows one to search for entry using filter string.&nbsp;&nbsp;
    Cofe.Data added the tags and description property which makes
    personalization possible, and they are searchable too: e.g.<br>
    root:{pictures} filetype:jpg OR filetype:png subdir:true tags:cofe<br>
    <h3>
      Two interfaces - ICofeSystemInfo and RESTApi</h3>
    Cofe's property based system enable the properties accessible locally
    (through ICofeSystemInfo) or via the web.&nbsp; The webapi returns Json
    which is suitable for consumption by the included javascript library
    (written with CoffeeScript, JQuery and Spine).&nbsp; Although I used the
    work "properties", it actually means both static values and runnable
    actions (in Actions, Funcs or Tasks), so one can CRUD entry and its
    resources, Executing an action using WebAPI, provided they are marked
    executable in web and sufficient permission.<br>
    <br>
    <h2>Cofe3.Core</h2>
    <h3>Property</h3>
    <p><a href="Cofe3.Property.png"><img style="font-family: monospace; width: 445px; height: 475px;"
          alt="Cofe3.Property" src="Cofe3.Property.png"></a></p>
    Every piece of data, every runnable method in an entry (ICofeSystemInfo
    and it's sub-interfaces) are property, for examples, we have
    CofeProperties.ParseName, we have
    CofeStreamProperties.<wbr>OpenReadStreamAsync.<br>
    <br>
    A group of related properties are reside in same enum, each property
    have an unique number, which can be lookup or reverse lookup using
    CofeServices.PropertyDictionary.<br>
    <br>
    One major reason of declaring them as enum is that I can define
    declarative attributes on them,&nbsp; which allows specifing output types
    (PRopertyType, Action, ListAction), how to obtain the value (GetFirst,
    HandleOnce, AggregateResult), additional check to output (FileSise,
    BitmapSize), alternative name (Alias), how to cache (CacheOptions) and
    how to expose via WebAPI (WebMetadata, WebResource and WebAction).&nbsp;
    Because they are frequently accessed, AttributeHelper
    (CofeServices.PropertyDictionary.AttributeHelper) provide cache access
    of these attributes.<br>
    <br>
    It's annoying to pass dozens of individual properties around, so there
    is PropertyDefinitions.&nbsp; PropertyDefinitions is a generic class that
    takes an enum (e.g. CofeDirectoryProperies).&nbsp; By default the
    PropertyDefinition include all properties in the specific category,
    using another constructor can filter some away.&nbsp; Cofe/CofeDB/CofeWS all
    have PropertyDefinitions static class, which contains all definitions
    defined in that library.&nbsp; PropertyDefinitionExpression is for lookup a
    property in database.<br>
    <br>
    A PropertyPair is a property and its value, PropertyPair.Value is in
    object type so you have to typed it using ValueAs&lt;T&gt;() method.&nbsp;
    PropertyPair.IsValid is for validating if the value matchs the
    PropertyTypeAttributes of the property/.<br>
    <br>
    <h4>PropertyDictionary</h4>
    PropertyDictionary (CofeServices.PropertyDictionary) is a dictionary for all
    PropertyDefinitions and Properties, you can lookup PropertyDefinitions from
    a property (GetPropertyDefinition), you can convert among property object,
    it's id and it's alias string (GetProperty/GetPropertyString), it also
    contains AttributeHelper and PropertyInvoker.<br>
    <br>
    <h5>PropertyDictionary.AttributeHelper</h5>
    AttributeHelper responsible for caching attributes in particular property,
    you can GetDefaultValue(), GetAlias(), GetProperty/DatabaseCacheOption() and
    FindAttribute&lt;T&gt;() from a property.<br>
    The most notable attribute for a property is PropertyTypeAttribute, it's
    used to indicate what value does it return, if an invalid value is returned,
    the PropertyPair's IsValid return false and this will fail when SetProperty.<br>
    <br>
    The most common use of PropertyTypeAttribute is to specify using typeof(T)
    and a default value, ActionAttribute derived PropertyTypeAttribute is used
    to represent an action, while ListActionAttribute and StreamActionAttribute
    derived from ActionAttribute is an action that return a specialized type .<br>
    <br>
    In Cofe both Bitmap and Stream are embedded in a container (BitmapContainer
    and StreamContainer), this is intended to allow one code to run both on
    Desktop and RT runtime, even one run using System.IO while another running
    WindowsStorage. <br>
    <h5>PropertyDictionary.PropertyInvoker</h5>
    <img alt="listAction" src="listAction_cs.jpg"><br>
    Given the output type, PropertyPair and ParameterDic, PropertyInvoker's
    Invoke&lt;T&gt;() and InvokeAsync&lt;T&gt;()&nbsp; (or without &lt;T&gt;)
    invoke an action/func or task in PropertyPair, and return the output value
    if there's one, in some case, the input ParameterDic also contain output
    value.&nbsp; PropertyInvoker can invoke an async property in sync mode or
    vice versa.<br>
    <br>
    Take a look to the flollowing, while the first and the second invokes an
    action from an entry, the third group actually get the action from the entry
    and use PropertyInvoker to execute it.&nbsp; The last one cast the action to
    appropriate output and run it as a task.<br>
    <br>
    <img alt="propinvoke" src="propinvoke_linq.jpg"><br>
    <h4>
      EntryFilters</h4>
    When listing in System.IO, you use a mask to indicate what you want to
    return, and SearchOption to indicate whether to search subdirectory.&nbsp;
    In Cofe, because of so many properties, you use EntryFilters.&nbsp;
    EntryFilters is used to filter a set of entries using a property.&nbsp;
    Consider a scenario like:<br>
    <div style="text-align: center;"><br>
      /api/parse?path={cofe}&amp;rel=list&amp;filterString=name:abc OR name:cde
      type:txt OR type:cs OR type:pas size:&gt;100 subdir:true</div>
    in web url, after parsing using IFilterParser, it converts to:<br>
    <br>
    <img alt="entryFilter" src="entryFilter_linq.jpg"><br>
    <br>
    When database is not enabled, EntryFilter's Match() method is used to
    return whether CurrentEntry is matched, and in the case of directory it
    returns whether to search sub-directory as well.&nbsp; Most property uses
    PropertyEntryFilter, which obtain the specified value from the entry.&nbsp;
    PropertyEntryFilter support &lt; and &gt; operator for numbers and
    datatime.<br>
    <br>
    While Matching sub-entries (whether to search subdirectory for entries) is
    OR based, matching entries in EntryFilters are matched in AND based, that
    means the entry is matched only if all EntryFilters return Yes.&nbsp; When
    or is needed, one have to embed it into OrEntryFilter, e.g.<br>
    new OrEntryFilter(new NameEntryFilter("abc"), new NameEntryFilter("cde")).<br>
    <br>
    When working with database, this becomes impossible, consider if I want
    to search the whole database for certain criteria, I'll have to
    materialize every entry in database and compare its values.&nbsp; This is
    the reason that in every PropertyDefinitions theres a
    PropertyExpressionDefinition, which provides expression
    (Func&lt;CofeSystemInfo, T&gt;) to lookup a variable in database.&nbsp; By
    default all properties is lookup from the PropertyPair_{type} table
    (where type depended on property type),&nbsp; if the property is cached in
    ORM, (e.g. ImageProperties.CameraMaker),<br>
    it have to be specify explicitly, for examples:<br>
    <br>
    <img alt="entryFilterExpression" src="entryFilterExpression_linq.jpg"><br>
    <h5>
      EntryOptionFilterParser</h5>
    EntryFilterParser (CofeServices.EntryFilterParser) is responsible to convert
    a Filter string to Entry/OptionFilters and vice versa, it construct filter
    using it's FilterConstructor.TryConstruct&lt;T&gt;() method, which ask
    lookup the appropriate IFilterFactory and construct from there.&nbsp;
    EntryFilterParser also contains PropertySerializer which responsible to
    convert a Key/Value pair to/from string.<br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <h3>
      Entry (CofeSystemInfo)</h3>
    <a href="Cofe3.Entry.png"><img style="width: 658px; height: 297px;" alt="Cofe3.Entry"
        src="Cofe3.Entry.png"></a><br>
    Entry in Cofe usually indicate the interface ICofeSystemInfo and it's
    child interface like IFileSystemInfo, IMediaInfo and IEntryTypeInfo.&nbsp;
    The base implementation of them is CofeSystemInfo.<br>
    <br>
    CofeSystemInfo and it's sub-implementations, like
    Cofe.IO.FileInfo, has two purpose.&nbsp; One is to implement the interfaces
    so user can access the properties and methods through the interfaces.&nbsp;
    The another is to participate in EntityFramework CodeFirst, so some of
    the properties can be written as named column in appropriate table,
    e.g. FileInfo(<span style="text-decoration:underline">Id</span>, <span style="text-decoration:underline">Type</span>,
    FileAttributes, Md5), instead of writing in a typed table, e.g.
    PropertyPair_Int16 (<span style="text-decoration:underline">PropertyId</span>,
    <span style="text-decoration:underline">Id</span>, ORMValue).<br>
    &nbsp;<br>
    Unlike System.IO, Cofe is designed to support other third-party plugins
    so it's not possible for a implementer to implement all possible
    interfaces, for examples, Cofe.IO support compression archives (via
    SevenZipSharp), so a FileInfo may be expected to implement
    IDirectoryInfo.&nbsp; This is not possible so I added a mechanism called
    IDynamicInterface, which adds Is&lt;T&gt;() and As&lt;T&gt;() methods
    to CofeSystemInfo, so if you call zipFile.As&lt;IDirectoryInfo&gt;(),
    you gets a DirectoryInfo which implements IDirectoryInfo.<br>
    <br>
    The construction of PropertyHost and specific type of ICofeSystemInfo is
    done in CofeServices.IEntryContructor....<br>
    <br>
    This may sound strange because the FileInfo shouldn't contain
    properties required in DirectoryInfo.&nbsp; The implementer of an entry is
    actually a skin.&nbsp; If you open FileInfo.cs you can find something like
    the following in the getter of a property:<br>
    return
    Properties.Behaviors.getProperty&lt;string&gt;(CofeStreamProperties.Md5).<br>
    <br>
    The Properties shown above is a IPropertyHost, which contains the
    cached variables and the means of getting, setting and invoking a
    noncached variables, so the process casting from File to DirectoryInfo
    is just create a blank DirectoryInfo and assign the PropertyHost object.<br>
    <br>
    <h4>
      PropertyHost</h4>
    <br>
    PropertHost is a host of all supported PropertyProvides, which includes
    PropertyCache, Primary and Secondary PropertyProviders.&nbsp; It also
    contains PropertyBehaviorManager (IPropertyHost.Behaviors), which
    responsible for getting, setting and invoking properties.<br>
    <br>
    There is one PrimaryPropertyProvider per PropertyHost, and it must
    provides all basic required properties for the entry to be functional,
    including ParseName and Type.&nbsp; In CofeIO implementation it's the
    SystemIOPropertyProvider (or WindowStoragePropertyProvider) that talks
    with underlying SystemIO.&nbsp; <br>
    <br>
    PropertyHost exists in a connected state or disconnected state.&nbsp; When
    connected, PrimaryPP is assigned and the general property access
    (get/set) strategy will be Cache -&gt; Primary -&gt; SecondaryPPs.&nbsp;
    When disconnected, PrimaryPP is null and the sequence becomes Cache
    -&gt; SecondaryPPs -&gt; Connect() -&gt; PrimaryPP.&nbsp; This allows
    properties from database written to PropertyCache and access from there
    without connecting to the backstore.<br>
    <br>
    SeconaryPropertyProviders (SeconaryPPs) are generated when PropertyHost
    is constructing, it ask every ISecondaryPropertyProviderFactory if they
    can create new PropertyProviders for this IPropertyHost.&nbsp; For examples,
    ExifPropertyProvider is attached when name is end with .jpg and
    CofeStreamProperties.OpenStreamAsync is supported.<br>
    <br>
    PropertyBehaviorManager responsible for access (get/set) and invoke of
    property.&nbsp; Based on InvokeBehaviorAttribute of the property, which
    included HandleOnes, ReturnMin/MaxOnly and AggregateResult), it create
    different IGetBehavior or ISetBehavior to handle the request.&nbsp; For
    invocation, it first get the property, depended on how it's defined in the
    attribute, it can be Action&lt;PD&gt;, Func&lt;PD, T&gt;, Func&lt;PD,
    Task&gt; or Func&lt;PD, Task&lt;T&gt;&gt;, depended on sync/async and
    whether have return value T.&nbsp; PD is ParameterDic, it is a string object
    dictionary, used for both passing input parameters and sometimes some
    additional output like IsHandled, or in the case of listing with pagination,
    TotalPages and TotalItems.&nbsp; Although PD is a string object dictionary,
    in CofeWS it support string parameter in url only.<br>
    <h4>Implementation:</h4>
    The implementation of entry properties are really verbose, if you want to
    implement your own entry types and properties, you have to:<br>
    <ol>
      <li>Define a property enum (e.g. CofeProperties), create property
        definition (CofePropertyDefinitions, which is
        PropertyDefinitionBase&lt;CofeProperties&gt;)</li>
      <li>Create PropertyProvider to return values (Primary for new entry types,
        Secondary for existing entry types)</li>
      <li>Define new interface (e.g. IFileInfo) and the means to provide the
        interface (IDynamicInterfaceProvider)</li>
      <li>Define the implements (FileInfo), participate in the Database table
        construction (IDatabaseConfigurator) and EntityFramework Migration.</li>
      <li>Define the volume factories and diretory listers.</li>
    </ol>
    I hope this can be automated a bit in future.<br>
    <br>
    <h4>EntryList : CofeDirectoryInfo</h4>
    <a href="Cofe3.Data.EntryList.png"><img style="width: 470px; height: 393px;"
        alt="entryList" src="Cofe3.Data.EntryList.png"></a><br>
    EntryList is for representing multiple entries, it's inherited from
    ICofeDirectoryInfo so you can use it's GetCofeSystemInfosAsync() or
    EnumerateCofeSystemInfos() methods to access it contents (actual content in
    auto, link in custom)<br>
    <br>
    There are two kinds of entry lists:<br>
    <br>
    Custom entry lists allow user to specify the content of an Entrylists by
    adding them manually.&nbsp; User calls ICustomEntryList.AddLink to link an
    entry to a CustomEntryList, which will create an IEntryLink object that have
    it's Entry property linked to the actual target entry.&nbsp; When database
    is enabled, this link is represented in DatabaseEntry, just like an entry. <br>
    <br>
    It's possible to create an entryLink to another CustomEntryList, this is
    what happened when you called IEntryList.CreateLinkFolder() method, it calls
    createAsync() and return an IEntryLink.<br>
    <br>
    CustomEntryList can be created using EntryFE.NewEntryList() method, once it
    is make parsable using EntryListFE.MakeParsable() method, it is accessible
    as a volume.<br>
    <br>
    AutoEntryList means the entries are automatically polled, using Entry and
    Option Filters.&nbsp; It's mainly for use in search.<br>
    <br>
    AutoEntryList can be created using EntryFE.SearchAsync() method with a
    filter string, the filter string is passed to IEntryOptionFilterParser to
    convert into Entry and OptionFilters.<br>
    <br>
    <h4>EntryTypeInfo&nbsp;</h4>
    There is a SystemVolume named {EntryTypeInfo} when Cofe starts, it represent
    the parent directory of all EntryTypes.&nbsp; EntryType is a way to define
    properties for broad range of entries,one entry can have one entry type
    only.&nbsp; In database, it's stored as a field in DatabaseEntry.<br>
    <br>
    When an EntryTypeInfo is requested via calling
    CofeEntryTypeProperties.EntryTypeKey or Info provided by
    EntryTypeSecondaryPP,&nbsp; it actually calls
    EntryTypeInfoManager.GetEntryTypeKey() method, which use it's
    IEntryTypeIdentifier to locate EntryTypeKey and IEntryTypeProvider to create
    PrimaryPP for the EntryType.&nbsp; It's then passed to EntryConstructor to
    construct as PropertyHost and entry.<br>
    <br>
    Because EntryTypeInfo is an entry, other external libraries can attach
    secondary PropertyProviders to the resulting PropertyHost.<br>
    <br>
    <h4>TransferManager</h4>
    <a href="Cofe3.EntryTransfer.png"><img style="width: 353px; height: 377px;"
        alt="EntryTransfer" src="Cofe3.EntryTransfer.png"></a><br>
    Except CofeDirectoryProperties.CreateAsync and
    CofeEntryLinkProperties.AddLink, entry itself do not provide commands for
    transferring entries.&nbsp; Instead, CofeServices.TransferManager is used
    for transfering entries to a directory.&nbsp; TransferManager has
    Transfer/RenameAsync() methods, when called, look for appropriate
    ITransferHelper to do the transfer.&nbsp; The default transfer included
    CopyDirectory, CopyStream, RenameEntry and LinkEntry, which do the transfer
    by entry by entry based.<br>
    <br>
    <br>
    <h3>Volume</h3>
    <a href="Cofe3.Volume.png"><img style="width: 705px; height: 350px;" alt="Cofe3.Volume"
        src="Cofe3.Volume.png"></a><br>
    Similar to System.IO, Volumes in Cofe are root root directories of every
    entry.&nbsp; But unlike SystemIO, whcih every volume represent a drive
    letter, Cofe does not have concept of drive, it's volumes are defined by
    it's implementation.&nbsp; For CofeIO, one use root directory as a
    volume.&nbsp; Because of this, when Cofe is started, there's a system volume
    (EntryTypeInfo), but there's no non-system volume in
    CofeServices.VolumeRegistrar, a place which keep track of registered volumes
    and volume factories.<br>
    <br>
    VolumeFactories are defined by it's implementation, Cofe3 has
    EntryListVolumeFactory for registering entrylist as volume, while CofeIO has
    SystemIO/WindowsStorageVolumeFactory for registering volume as disk
    path.&nbsp; If a volume is registered (e.g. using
    VolumeFE.MapAsync("CofeIO", "doc", Tuple.Create("VolumePath", RootPath))), a
    Volume is created with VolumeInfo, DirectoryLister and EventSources.
    which&nbsp; after that {doc} directory can then be parsable as
    CofeDirectory.<br>
    <br>
    DirectoryLister is a class that provide the root PropertyProviders of a
    volume, and parse a partial path (a path without volume id) and return
    appropriate PrimaryPropertyProvider if found.<br>
    EventSources is for monitoring events happened in the particular file
    system, when the volume is registered, it's event source will be registered
    in EventHub so when the source raise a event, it passed to the hub and
    distribute to the listeners.<br>
    <h4>CofeSettings</h4>
    The registed VolueInfos can be reused when the application is restarted if
    it's stored in CofeSettings.&nbsp; CofeServices.CofeSettings is a central
    place to store all settable settings (there's also ICofeWsSettings too,
    which inherits ICofeSettings), one can update the property and call
    CofeServices.SaveAsync(), which will notify all ISaveSettings implementers
    to save settings to CofeSettings, and all settings will be serialized in
    _cofe.xml in IsolatedStorage folder.&nbsp; And when
    CofeServices.RestoreAsync() is called, it will deserialized the settings and
    notify all IRestoreSettings implmenters to restore settings.&nbsp;
    Volume(IVolume) are stored as IVolumeInfo, which contains FactoryId,
    VolumeId and parameter only.<br>
    <h4>Security</h4>
    Security can be setup on user role and volume only, not it's sub-directory,
    so if you want to assign different role requirement for different
    sub-directory, you will have to create multiple volumes.&nbsp; VolumeFE
    helper contains GrantPermission(role, volumeId, permission) and
    RevokePermission(), which calls
    CofeServices.SecurityManager.Add/RemovePermissionPolicy() with appropriate
    parameters.<br>
    <br>
    When an entry is parsed, listed, a property or action is called or invoked,
    CofeServices.SecurityManager.DemandPermission&lt;IVolumeInfo&gt;() is
    called, and SecurityManager will evaluate it's PermissionPolicy.&nbsp; If
    there's any match, it will check PermissionPolicy's Required role, and check
    if the role is included in ISecurityManager.CurrentPrincipal, which is
    generated by
    ISecurityManager.UserManager.GetPrincipal(CurrentThread.Identity).<br>
    <br>
    Reading, Writing and Invoking normally require respective permission
    (R/W/E), but it can be overrided by using RequirePermirrionAttribute.&nbsp;
    The check take place mostly in PropertyBehaviorManager.&nbsp; The security
    is designed to deny unauthorize access on web only;&nbsp; after all, if
    someone has access to the source code they don't need Cofe to access the
    data they wanted.<br>
    <br>
    While SecurityManager holds IPermissionPolicy, UserManager holds
    IPrincipalPolicy, which defined what role is given to what identity, one can
    use AddPrincipalPolicy() and RemovePrincipalPolicy() to update the
    PrincipalPolicies.&nbsp; <br>
    <br>
    The main use of security is in CofeWs.&nbsp; In WSUserManager, if the user
    identity is in Forms Authentication, UserManager calls ASP.Net's
    SimpleRoleProvider for roles, thus you just need to manager the roles in
    ASP.Net instead of setting PrincipalPolicies.<br>
    <br>
    Both IPermissionPolicy and IPrincipalPolicy are saved in CofeSettings.<br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <h3>
      Events</h3>
    Event handling for Cofe system changes is important because to enable
    CofeSystemWatcher to report when there's an event, to keep database updated,
    and to keep the feed system work, it has to be notified when there's a
    change.&nbsp; <br>
    <br>
    In Cofe event handling is done by EventHub, it's a hub that accept event and
    broadcast multiple events in an interval.&nbsp; On one side, one can use
    it's RegisterEventSource&lt;T&gt;() to register an IEventSource&lt;T&gt;,
    which raise a specific type of EventArgs.&nbsp; For examples, each Volume
    may contains one or more EventSources to raise&nbsp; CofeSystemEvent when an
    item in the volume is changed, (e.g. SystemIOEventSource).&nbsp; The default
    implementation of Volume is ManualVolumeSource, which allow user to raise
    event by calling the RaiseChange/Created/Renamed/Deleted() methods.<br>
    <br>
    On the another side, one can use it's RegisterEventListener&lt;T&gt;() to
    register an IEventListenener&lt;T&gt;.&nbsp; EventHub rountinely broadcast
    received events to the listeners every n secondsusing IEventListener's
    DisplatchEvent(). <br>
    <br>
    Both IEventSource and IEventListener can unregister itself by using their
    CancellationToken. <br>
    <br>
    A list of EventListener implementations : <br>
    <ul>
      <li><strong>CofeSystemWatcher</strong> - Just like FileSystemWatcher,
        there's a CofeSystemWater to raise event when CofeSystem changes,
        CofeSystemWatcher only register itself to EventHub when
        EnableRaisingEvents is set to true.</li>
      <li><strong>CofeDBEventListener</strong> - CofeDBEventListener has a
        IEventListenerFactory which enable it to be registered when the first
        event is dispatched, it refresh the LastUpdateTime/LastListTime of the
        Parent of the affected entry, expire the affected entry, and update the
        entry if CacheUpdateOption is Passive.</li>
      <li><strong>FeedServices </strong>- CofeWsServices.FeedServices write fee
        periodically to \log directory using atom rss format.</li>
    </ul>
    <h3>
      <wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr><wbr>CofeServices</h3>
    <a href="Cofe3.CofeServices.png"><img style="width: 515px; height: 277px;" alt="Cofe3.CofeServices"
        src="Cofe3.CofeServices.png"></a>
    <p>CofeServices is a static class that provide to all ICofeServices.&nbsp;
      The IEntryConstructor, IPathParserManager and ISecurityManaager are all
      ICofeServices.&nbsp; CofeServices contains ServiceLocater, which
      responsible for holding and querying ICofeServices.</p>
    <h4>BootStrapper </h4>
    <p>BootStrapper register all related ICofeServices to the
      ServiceLocater.&nbsp; ManualBootStrapper and MEFBootStrapper is now
      replaced by ModuleBootStrapper; all library contains a class named
      RegisterModule, which contains the ICofeServices in that library, all you
      needed is to include these RegisterModule in the Constructor of the
      BootStrapper (sequence doesn't matter, as ICofeServices are sorted when
      imported) and invoke it's Run() method.</p>
    <p>new ManualBootStrapperIODB(false, false).Run();&nbsp;&nbsp;&nbsp;
      &nbsp;&nbsp;&nbsp;&nbsp; </p>
    <p>new ModuleBootStrapper(<br>
      &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; true, <br>
      &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; new Cofe.Core.RegisterModule(),<br>
      &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; new Cofe.Data.RegisterModule(true,
      CacheUpdateOptions.Manual),<br>
      &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; new Cofe.IO.RegisterModule(),<br>
      &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; new Cofe.Media.RegisterModule()<br>
      &nbsp;&nbsp;&nbsp; ).Run();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
    <p><br>
    </p>
    <p>Beside CofeServices, there's CofeDBServices and CofeWsServices as well,
      which provides services defined in Cofe.Data and Cofe.Web.</p>
    <p><br>
    </p>
    <p><br>
    </p>
    <p><br>
    </p>
    <h2>Cofe3.Data</h2>
    <a href="Cofe3.Data.png"><img style="width: 835px; height: 312px;" alt="Cofe3.Data"
        src="Cofe3.Data.png"></a>
    <p>CofeDB3 is responsible for caching entries in database, allow parsing,
      listing and searching entries in database.</p>
    <h3>IRepository (Pending to be refactored)</h3>
    <p>When you call DatabaseProperties.WriteRepositoryAsync action to add or
      update an entry to the database, you are actually calling
      IRepository.AddOrUpdate().&nbsp; IRepository talks with the database
      implementation and it provide update, lookup and remove for DatabaseEntry.</p>
    <p>CofeDBServices.RepositoryFactory is for constructing IRepository, which
      responsible for reading, writing and removing entries (in PropertyHost
      form) to and from database.&nbsp; A factory is required because each copy
      of EFRepository, the implementation of IRepository that uses
      EntityFramework, contains a DbContext, which must be released when
      EFRepository is disposed.</p>
    <p>using (var Repository = RepositoryFacoty.CreateRepository())<br>
      {... }</p>
    <a href="dbstructure_sql.jpg"><img style="width: 431px; height: 239px;" alt="dbstructure"
        src="dbstructure_sql.jpg"></a>
    <h4>DatabaseEntry</h4>
    <p>Because one entry may have more than one implementations, the most
      logical way is to have a common parent class to hold these implementations
      and this is DatabaseEntry.&nbsp; This class is defined in Cofe.Core,
      contains implementation which contains CofeSystemInfos, and
      ExtendedProperties, which contains non-ORM (Object Relational Mapping,
      e.g. FileInfo.Length) property values.&nbsp; To reduce redundancy,
      properties in CofeSystemInfo, like ParseName, Label, CofeAttributes, are
      moved to DatabaseEntry.</p>
    <h4>DatabaseEntryFactory </h4>
    <p>But how to construct these DatabaseEntries?
      CofeDBServices.DatabaseEntryFactory provide a bi-direction way to
      construct them.&nbsp; </p>
    <p>When constructing from IPropertyHost to DatabaseEntry (to EF),
      DatabaseEntryFactory ask PropertyInterfaceManager for each castable types,
      and create an Implementation (e.g. FileInfo) when applicable.&nbsp; This
      part is easy because it's the EF side that needed only the ORM property
      for each implementations.&nbsp; </p>
    <p>When constructing from DatabaseEntry (from EF) to IPropertyHost, each
      implementation has a PropertyCache that only cached the ORM properties,
      while the non-ORM properties is in ExtendedProperties.
      DatabaseEntryFactory have to group the PropertyCache into one, write the
      properties in the root DatabaseEntry and ExtendedProperties to the
      PropertyCache.</p>
    <h4>DatabaseConfigurator</h4>
    <p>The DbContext of Cofe is CofeDbContext, to allow external library to
      participate when constructing the model and database, CofeDbContext loop
      through each IDatabaseConfigurator when ModelCreating.&nbsp; <br>
      The construction of CofeDbContext initiator is best through
      CofeDbContext.Initializer, which uses migration to create the database.</p>
    <img alt="migration" src="migration_cs.jpg">
    <p>I have manually modified the migration file after it's automatically
      generated, added casade delete to the child classes of CofeSystemInfo and
      PropertyPair so child row is deleted with the parent row, instead of
      complaining child row is linking the parent row.&nbsp; I also added a sql
      statement which makes DatabaseEntry.ParseName unique.</p>
    <p>A list of commands in Migrations\CommandLine.txt is available for how to
      generate migrations and use it to update database manually.</p>
    <p><br>
    </p>
    <h2>Cofe3.Web</h2>
    <a href="Cofe3.Web.png"><img style="width: 648px; height: 429px;" alt="Cofe3.Web.png"
        src="Cofe3.Web.png"></a><br>
    <br>
    Cofe3.Web provide Feed (FeedServices) and Serialization/Deserialization
    services (HttpMessageFormatterManager) to Cofe.Web.Services, while
    Cofe.Web.Services library is an ASP.Net application that include the
    APIController for serving the web services.<br>
    <br>
    <h3>FeedServices</h3>
    Use CofeWsServices.Feeds.GetFeedServices&lt;T&gt;() to access the
    IFeedServices&lt;T&gt; object, which contains anything related to a feed for
    particular (T) typed of EventArgs.&nbsp; FeedServices contains
    FeedFileSystemHelper, FeedUpdater, FeedBuilder and FeedLinkHelper and
    FeedRepository.<br>
    <br>
    <h4>FeedFileSystemHelper</h4>
    FeedServices uses FeedFileSystemHelper is a class for providing IO access,
    the actual path of feeds are also defined in the class..<br>
    <h4>FeedUpdater </h4>
    When FeedServices.Updater (IFeedUpdater) is started, it listen to events
    from EventHub and write to it's EventBuffer, which store
    EventLog&lt;EventArgs&gt;.&nbsp; Once every n seconds, it calls it's Update
    thread, which Run the script command QueryingEventsCommand&lt;T&gt; shown on
    the diagram.&nbsp; When this command completed it will emit
    IdentifyingRecentEventFeedsCommand&lt;T&gt;, and it will be run and so on,
    until NotifyingListeners&lt;T&gt;, which will emit ResultComamnds.OK, and
    thus stop the script command. This implementation is taken from the book
    (REST in Practice)<br>
    <h4>FeedBuilder</h4>
    FeedBuilder is responsible for loading and building the
    Feed&lt;T&gt;.&nbsp;&nbsp; There is always one RecentEventFeed and
    ArchiveFeeds, which RecentEventFeed is full it is copied as ArchiveFeed, and
    a new RecentEventFeed is created.&nbsp; These feed are stored in
    \log\current (as {FeedGuid}.atom) and \log\archive (as {Feed#}.atom) as atom
    file by default.&nbsp; For each feed item added, the entry is written
    separately in \log\entry (as {EntryGuid}.atom).&nbsp; <br>
    <br>
    Feed object contains FromFileName(), FromFeedId() and Save() methods to load
    and save SyndicationFeed object.<br>
    <h4>FeedLinkHelper</h4>
    Given LinkType and Link formation, FeedLinkHelper creates SyndicationLink
    object for added to the result SyndicationFeed, based on the template
    defined in FeedBuilder.&nbsp; Creatable links included Entry
    (CofeSystemInfo), Event (SyndicationItem) and Feed (SyndicationFeed).<br>
    <h4>FeedRepository</h4>
    FeedRepository is for external classes to access Event and Feed stored in
    memory and file system.&nbsp; FeedRepository returns SyndicationItem or
    Feed, one can serialize the object themselves, or use the included
    CofeServices.HttpMessageFormatter to create HttpResponseMessage.<br>
    <br>
    <h3>HttpMessageFormatterManager</h3>
    CofeServices.HttpMessageFormatter is responsible for both Serialize and
    Deserialize entry to HttpResponseMessage or from HttpRequestMessage, which
    are used in WebAPI.&nbsp; The manager class is a interface class, the actual
    conversion is done by implemented IHttpRequestMessageFormatter and
    IHttpResponseFormatter.&nbsp; <br>
    <br>
    <br>
    <br>
    <br>
    <br>
    <h3> </h3>
  </body>
</html>
